﻿using Engine;
using Engine.Graphics;
using Engine.Input;
using System;

namespace SCPAK2
{
    public enum DragMode
    {
        AllItems,
        SingleItem
    }
    [Flags]
    public enum WidgetInputDevice
    {
        None = 0x0,
        Keyboard = 0x1,
        Mouse = 0x2,
        Touch = 0x4,
        GamePad1 = 0x8,
        GamePad2 = 0x10,
        GamePad3 = 0x20,
        GamePad4 = 0x40,
        Gamepads = 0x78,
        VrControllers = 0x80,
        All = 0xFF
    }
    public struct Segment2
    {
        public Vector2 Start;

        public Vector2 End;

        public Segment2(Vector2 start, Vector2 end)
        {
            Start = start;
            End = end;
        }

        public override string ToString()
        {
            return $"{Start.X}, {Start.Y},  {End.X}, {End.Y}";
        }

        public static float Distance(Segment2 s, Vector2 p)
        {
            Vector2 v = s.End - s.Start;
            Vector2 v2 = s.Start - p;
            Vector2 v3 = s.End - p;
            float num = Vector2.Dot(v2, v);
            if (num * Vector2.Dot(v3, v) <= 0f)
            {
                float num2 = v.LengthSquared();
                if (num2 == 0f)
                {
                    return Vector2.Distance(p, s.Start);
                }
                return MathUtils.Abs(Vector2.Cross(p - s.Start, v)) / MathUtils.Sqrt(num2);
            }
            if (!(num > 0f))
            {
                return v3.Length();
            }
            return v2.Length();
        }

        public static bool Intersection(Segment2 s1, Segment2 s2, out Vector2 result)
        {
            Vector2 v = s1.End - s1.Start;
            Vector2 v2 = s2.End - s2.Start;
            float num = Vector2.Cross(v, v2);
            if (num == 0f)
            {
                result = Vector2.Zero;
                return false;
            }
            float num2 = 1f / num;
            float num3 = (v2.X * (s1.Start.Y - s2.Start.Y) - v2.Y * (s1.Start.X - s2.Start.X)) * num2;
            float num4 = (v.X * (s1.Start.Y - s2.Start.Y) - v.Y * (s1.Start.X - s2.Start.X)) * num2;
            if (num3 < 0f || num3 > 1f || num4 < 0f || num4 > 1f)
            {
                result = Vector2.Zero;
                return false;
            }
            result = new Vector2(s1.Start.X + num3 * v.X, s1.Start.Y + num3 * v.Y);
            return true;
        }

        public static Vector2 NearestPoint(Segment2 s, Vector2 p)
        {
            Vector2 v = s.End - s.Start;
            Vector2 v2 = s.Start - p;
            Vector2 v3 = s.End - p;
            float num = Vector2.Dot(v2, v);
            if (num * Vector2.Dot(v3, v) <= 0f)
            {
                float num2 = v.Length();
                if (num2 == 0f)
                {
                    return s.Start;
                }
                float num3 = MathUtils.Sqrt(v2.LengthSquared() - MathUtils.Sqr(MathUtils.Abs(Vector2.Cross(p - s.Start, v)) / num2));
                return Vector2.Lerp(s.Start, s.End, num3 / num2);
            }
            if (!(num > 0f))
            {
                return s.End;
            }
            return s.Start;
        }
    }
    public class WidgetInput
    {
        public bool m_isCleared;

        public Widget m_widget;

        public Vector2 m_softMouseCursorPosition;

        public Vector2? m_mouseDownPoint;

        public MouseButton m_mouseDownButton;

        public double m_mouseDragTime;

        public bool m_mouseDragInProgress;

        public bool m_mouseHoldInProgress;

        public bool m_isMouseCursorVisible = true;

        public bool m_useSoftMouseCursor;

        public int? m_touchId;

        public bool m_touchCleared;

        public Vector2 m_touchStartPoint;

        public double m_touchStartTime;

        public bool m_touchDragInProgress;

        public bool m_touchHoldInProgress;

        public Vector2 m_padCursorPosition;

        public Vector2? m_padDownPoint;

        public double m_padDragTime;

        public bool m_padDragInProgress;

        public bool m_isPadCursorVisible = true;

        public Vector2? m_vrDownPoint;

        public double m_vrDragTime;

        public bool m_vrDragInProgress;

        public bool m_isVrCursorVisible = true;

        public bool Any
        {
            get;
            set;
        }

        public bool Ok
        {
            get;
            set;
        }

        public bool Cancel
        {
            get;
            set;
        }

        public bool Back
        {
            get;
            set;
        }

        public bool Left
        {
            get;
            set;
        }

        public bool Right
        {
            get;
            set;
        }

        public bool Up
        {
            get;
            set;
        }

        public bool Down
        {
            get;
            set;
        }

        public Vector2? Press
        {
            get;
            set;
        }

        public Vector2? Tap
        {
            get;
            set;
        }

        public Segment2? Click
        {
            get;
            set;
        }

        public Segment2? SpecialClick
        {
            get;
            set;
        }

        public Vector2? Drag
        {
            get;
            set;
        }

        public DragMode DragMode
        {
            get;
            set;
        }

        public Vector2? Hold
        {
            get;
            set;
        }

        public float HoldTime
        {
            get;
            set;
        }

        public Vector3? Scroll
        {
            get;
            set;
        }

        public Key? LastKey
        {
            get
            {
                if (m_isCleared || (Devices & WidgetInputDevice.Keyboard) == 0)
                {
                    return null;
                }
                return Keyboard.LastKey;
            }
        }

        public char? LastChar
        {
            get
            {
                if (m_isCleared || (Devices & WidgetInputDevice.Keyboard) == 0)
                {
                    return null;
                }
                return Keyboard.LastChar;
            }
        }

        public bool UseSoftMouseCursor
        {
            get
            {
                return m_useSoftMouseCursor;
            }
            set
            {
                m_useSoftMouseCursor = value;
            }
        }

        public bool IsMouseCursorVisible
        {
            get
            {
                if ((Devices & WidgetInputDevice.Mouse) == 0)
                {
                    return false;
                }
                return m_isMouseCursorVisible;
            }
            set
            {
                m_isMouseCursorVisible = value;
            }
        }

        public Vector2? MousePosition
        {
            get
            {
                if (!m_isCleared && (Devices & WidgetInputDevice.Mouse) != 0)
                {
                    if (m_useSoftMouseCursor)
                    {
                        return m_softMouseCursorPosition;
                    }
                    if (!Mouse.MousePosition.HasValue)
                    {
                        return null;
                    }
                    return new Vector2(Mouse.MousePosition.Value);
                }
                return null;
            }
            set
            {
                if ((Devices & WidgetInputDevice.Mouse) == 0 || !value.HasValue)
                {
                    return;
                }
                if (m_useSoftMouseCursor)
                {
                    Vector2 vector;
                    Vector2 vector2;
                    if (Widget != null)
                    {
                        vector = Widget.GlobalBounds.Min;
                        vector2 = Widget.GlobalBounds.Max;
                    }
                    else
                    {
                        vector = Vector2.Zero;
                        vector2 = new Vector2(Window.Size);
                    }
                    m_softMouseCursorPosition = new Vector2(MathUtils.Clamp(value.Value.X, vector.X, vector2.X - 1f), MathUtils.Clamp(value.Value.Y, vector.Y, vector2.Y - 1f));
                }
                else
                {
                    Mouse.SetMousePosition((int)value.Value.X, (int)value.Value.Y);
                }
            }
        }

        public Point2 MouseMovement
        {
            get
            {
                if (!m_isCleared && (Devices & WidgetInputDevice.Mouse) != 0)
                {
                    return Mouse.MouseMovement;
                }
                return Point2.Zero;
            }
        }

        public int MouseWheelMovement
        {
            get
            {
                if (!m_isCleared && (Devices & WidgetInputDevice.Mouse) != 0)
                {
                    return Mouse.MouseWheelMovement;
                }
                return 0;
            }
        }

        public bool IsPadCursorVisible
        {
            get
            {
                if (m_isPadCursorVisible)
                {
                    if (((Devices & WidgetInputDevice.GamePad1) == 0 || !GamePad.IsConnected(0)) && ((Devices & WidgetInputDevice.GamePad2) == 0 || !GamePad.IsConnected(1)) && ((Devices & WidgetInputDevice.GamePad3) == 0 || !GamePad.IsConnected(2)))
                    {
                        if ((Devices & WidgetInputDevice.GamePad4) != 0)
                        {
                            return GamePad.IsConnected(3);
                        }
                        return false;
                    }
                    return true;
                }
                return false;
            }
            set
            {
                m_isPadCursorVisible = value;
            }
        }

        public Vector2 PadCursorPosition
        {
            get
            {
                return m_padCursorPosition;
            }
            set
            {
                Vector2 vector;
                Vector2 vector2;
                if (Widget != null)
                {
                    vector = Widget.GlobalBounds.Min;
                    vector2 = Widget.GlobalBounds.Max;
                }
                else
                {
                    vector = Vector2.Zero;
                    vector2 = new Vector2(Window.Size);
                }
                value.X = MathUtils.Clamp(value.X, vector.X, vector2.X - 1f);
                value.Y = MathUtils.Clamp(value.Y, vector.Y, vector2.Y - 1f);
                m_padCursorPosition = value;
            }
        }

        public ReadOnlyList<TouchLocation> TouchLocations
        {
            get
            {
                if (!m_isCleared && (Devices & WidgetInputDevice.Touch) != 0)
                {
                    return Touch.TouchLocations;
                }
                return ReadOnlyList<TouchLocation>.Empty;
            }
        }

        public Matrix? VrQuadMatrix
        {
            get;
            set;
        }
        public Vector2? VrCursorPosition
        {
            get;
            set;
        }

        public static WidgetInput EmptyInput
        {
            get;
        } = new WidgetInput(WidgetInputDevice.None);


        public Widget Widget => m_widget;

        public WidgetInputDevice Devices
        {
            get;
            set;
        }

        public bool IsKeyDown(Key key)
        {
            if (!m_isCleared && (Devices & WidgetInputDevice.Keyboard) != 0)
            {
                return Keyboard.IsKeyDown(key);
            }
            return false;
        }

        public bool IsKeyDownOnce(Key key)
        {
            if (!m_isCleared && (Devices & WidgetInputDevice.Keyboard) != 0)
            {
                return Keyboard.IsKeyDownOnce(key);
            }
            return false;
        }

        public bool IsKeyDownRepeat(Key key)
        {
            if (!m_isCleared && (Devices & WidgetInputDevice.Keyboard) != 0)
            {
                return Keyboard.IsKeyDownRepeat(key);
            }
            return false;
        }

        public void EnterText(ContainerWidget parentWidget, string title, string text, int maxLength, Action<string> handler)
        {
            Keyboard.ShowKeyboard(title, string.Empty, text, passwordMode: false, delegate (string s)
            {
                if (s.Length > maxLength)
                {
                    s = s.Substring(0, maxLength);
                }
                handler(s);
            }, delegate
            {
                handler(null);
            });
        }

        public bool IsMouseButtonDown(MouseButton button)
        {
            if (!m_isCleared && (Devices & WidgetInputDevice.Mouse) != 0)
            {
                return Mouse.IsMouseButtonDown(button);
            }
            return false;
        }

        public bool IsMouseButtonDownOnce(MouseButton button)
        {
            if (!m_isCleared && (Devices & WidgetInputDevice.Mouse) != 0)
            {
                return Mouse.IsMouseButtonDownOnce(button);
            }
            return false;
        }

        public Vector2 GetPadStickPosition(GamePadStick stick, float deadZone = 0f)
        {
            if (m_isCleared)
            {
                return Vector2.Zero;
            }
            Vector2 zero = Vector2.Zero;
            for (int i = 0; i < 4; i++)
            {
                if (((int)Devices & (8 << i)) != 0)
                {
                    zero += GamePad.GetStickPosition(i, stick, deadZone);
                }
            }
            if (!(zero.LengthSquared() > 1f))
            {
                return zero;
            }
            return Vector2.Normalize(zero);
        }

        public float GetPadTriggerPosition(GamePadTrigger trigger, float deadZone = 0f)
        {
            if (m_isCleared)
            {
                return 0f;
            }
            float num = 0f;
            for (int i = 0; i < 4; i++)
            {
                if (((int)Devices & (8 << i)) != 0)
                {
                    num += GamePad.GetTriggerPosition(i, trigger, deadZone);
                }
            }
            return MathUtils.Min(num, 1f);
        }

        public bool IsPadButtonDown(GamePadButton button)
        {
            if (m_isCleared)
            {
                return false;
            }
            for (int i = 0; i < 4; i++)
            {
                if (((int)Devices & (8 << i)) != 0 && GamePad.IsButtonDown(i, button))
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsPadButtonDownOnce(GamePadButton button)
        {
            if (m_isCleared)
            {
                return false;
            }
            for (int i = 0; i < 4; i++)
            {
                if (((int)Devices & (8 << i)) != 0 && GamePad.IsButtonDownOnce(i, button))
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsPadButtonDownRepeat(GamePadButton button)
        {
            if (m_isCleared)
            {
                return false;
            }
            for (int i = 0; i < 4; i++)
            {
                if (((int)Devices & (8 << i)) != 0 && GamePad.IsButtonDownRepeat(i, button))
                {
                    return true;
                }
            }
            return false;
        }

        public WidgetInput(WidgetInputDevice devices = WidgetInputDevice.All)
        {
            Devices = devices;
        }

        public void Clear()
        {
            m_isCleared = true;
            m_mouseDownPoint = null;
            m_mouseDragInProgress = false;
            m_touchCleared = true;
            m_padDownPoint = null;
            m_padDragInProgress = false;
            m_vrDownPoint = null;
            m_vrDragInProgress = false;
            ClearInput();
        }

        public void Update()
        {
            m_isCleared = false;
            ClearInput();
            if (Window.IsActive)
            {
                if ((Devices & WidgetInputDevice.Keyboard) != 0)
                {
                    UpdateInputFromKeyboard();
                }
                if ((Devices & WidgetInputDevice.Mouse) != 0)
                {
                    UpdateInputFromMouse();
                }
                if ((Devices & WidgetInputDevice.Gamepads) != 0)
                {
                    UpdateInputFromGamepads();
                }
                if ((Devices & WidgetInputDevice.Touch) != 0)
                {
                    UpdateInputFromTouch();
                }
            }
        }

        public void Draw(Widget.DrawContext dc)
        {

        }

        public void ClearInput()
        {
            Any = false;
            Ok = false;
            Cancel = false;
            Back = false;
            Left = false;
            Right = false;
            Up = false;
            Down = false;
            Press = null;
            Tap = null;
            Click = null;
            SpecialClick = null;
            Drag = null;
            DragMode = DragMode.AllItems;
            Hold = null;
            HoldTime = 0f;
            Scroll = null;
        }

        public void UpdateInputFromKeyboard()
        {
            if (LastKey.HasValue && LastKey != Key.Escape)
            {
                Any = true;
            }
            if (IsKeyDownOnce(Key.Escape))
            {
                Back = true;
                Cancel = true;
            }
            if (IsKeyDownRepeat(Key.LeftArrow))
            {
                Left = true;
            }
            if (IsKeyDownRepeat(Key.RightArrow))
            {
                Right = true;
            }
            if (IsKeyDownRepeat(Key.UpArrow))
            {
                Up = true;
            }
            if (IsKeyDownRepeat(Key.DownArrow))
            {
                Down = true;
            }
            Back |= Keyboard.IsKeyDownOnce(Key.Back);
        }

        public void UpdateInputFromMouse()
        {
            if (IsMouseButtonDownOnce(MouseButton.Left))
            {
                Any = true;
            }
            if (IsMouseCursorVisible && MousePosition.HasValue)
            {
                Vector2 value = MousePosition.Value;
                if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right))
                {
                    Press = value;
                }
                if (IsMouseButtonDownOnce(MouseButton.Left) || IsMouseButtonDownOnce(MouseButton.Right))
                {
                    Tap = value;
                    m_mouseDownPoint = value;
                    m_mouseDownButton = ((!IsMouseButtonDownOnce(MouseButton.Left)) ? MouseButton.Right : MouseButton.Left);
                    m_mouseDragTime = Time.FrameStartTime;
                }
                if (!IsMouseButtonDown(MouseButton.Left) && m_mouseDownPoint.HasValue && m_mouseDownButton == MouseButton.Left)
                {
                    if (IsKeyDown(Key.Shift))
                    {
                        SpecialClick = new Segment2(m_mouseDownPoint.Value, value);
                    }
                    else
                    {
                        Click = new Segment2(m_mouseDownPoint.Value, value);
                    }
                }
                if (!IsMouseButtonDown(MouseButton.Right) && m_mouseDownPoint.HasValue && m_mouseDownButton == MouseButton.Right)
                {
                    SpecialClick = new Segment2(m_mouseDownPoint.Value, value);
                }
                if (MouseWheelMovement != 0)
                {
                    Scroll = new Vector3(value, MouseWheelMovement / 120f);
                }
                if (m_mouseHoldInProgress && m_mouseDownPoint.HasValue)
                {
                    Hold = m_mouseDownPoint.Value;
                    HoldTime = (float)(Time.FrameStartTime - m_mouseDragTime);
                }
                if (m_mouseDragInProgress)
                {
                    Drag = value;
                }
                else if ((IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right)) && m_mouseDownPoint.HasValue)
                {
                    //拖动
                }
            }
            if (!IsMouseButtonDown(MouseButton.Left) && !IsMouseButtonDown(MouseButton.Right))
            {
                m_mouseDragInProgress = false;
                m_mouseHoldInProgress = false;
                m_mouseDownPoint = null;
            }
            if (m_useSoftMouseCursor && IsMouseCursorVisible)
            {
                MousePosition = (MousePosition ?? Vector2.Zero) + new Vector2(MouseMovement);
            }
        }

        public void UpdateInputFromGamepads()
        {
            if (IsPadButtonDownRepeat(GamePadButton.DPadLeft))
            {
                Left = true;
            }
            if (IsPadButtonDownRepeat(GamePadButton.DPadRight))
            {
                Right = true;
            }
            if (IsPadButtonDownRepeat(GamePadButton.DPadUp))
            {
                Up = true;
            }
            if (IsPadButtonDownRepeat(GamePadButton.DPadDown))
            {
                Down = true;
            }
            if (IsPadCursorVisible)
            {
                if (IsPadButtonDownRepeat(GamePadButton.DPadUp))
                {
                    Scroll = new Vector3(PadCursorPosition, 1f);
                }
                if (IsPadButtonDownRepeat(GamePadButton.DPadDown))
                {
                    Scroll = new Vector3(PadCursorPosition, -1f);
                }
                if (IsPadButtonDown(GamePadButton.A))
                {
                    Press = PadCursorPosition;
                }
                if (IsPadButtonDownOnce(GamePadButton.A))
                {
                    Ok = true;
                    Tap = PadCursorPosition;
                    m_padDownPoint = PadCursorPosition;
                    m_padDragTime = Time.FrameStartTime;
                }
                if (!IsPadButtonDown(GamePadButton.A) && m_padDownPoint.HasValue)
                {
                    if (GetPadTriggerPosition(GamePadTrigger.Left) > 0.5f)
                    {
                        SpecialClick = new Segment2(m_padDownPoint.Value, PadCursorPosition);
                    }
                    else
                    {
                        Click = new Segment2(m_padDownPoint.Value, PadCursorPosition);
                    }
                }
            }
            if (IsPadButtonDownOnce(GamePadButton.A) || IsPadButtonDownOnce(GamePadButton.B) || IsPadButtonDownOnce(GamePadButton.X) || IsPadButtonDownOnce(GamePadButton.Y))
            {
                Any = true;
            }
            if (!IsPadButtonDown(GamePadButton.A))
            {
                m_padDragInProgress = false;
                m_padDownPoint = null;
            }
            if (IsPadButtonDownOnce(GamePadButton.B))
            {
                Cancel = true;
            }
            if (IsPadButtonDownOnce(GamePadButton.Back))
            {
                Back = true;
            }
            if (m_padDragInProgress)
            {
                Drag = PadCursorPosition;
            }
            else if (IsPadButtonDown(GamePadButton.A) && m_padDownPoint.HasValue)
            {
            }
        }

        public void UpdateInputFromTouch()
        {
            foreach (TouchLocation touchLocation in TouchLocations)
            {
                if (touchLocation.State == TouchLocationState.Pressed)
                {
                    if (Widget.HitTest(touchLocation.Position))
                    {
                        Any = true;
                        Tap = touchLocation.Position;
                        Press = touchLocation.Position;
                        m_touchStartPoint = touchLocation.Position;
                        m_touchId = touchLocation.Id;
                        m_touchCleared = false;
                        m_touchStartTime = Time.FrameStartTime;
                        m_touchDragInProgress = false;
                        m_touchHoldInProgress = false;
                    }
                }
                else if (touchLocation.State == TouchLocationState.Moved)
                {
                    if (m_touchId == touchLocation.Id)
                    {
                        Press = touchLocation.Position;
                        if (!m_touchCleared)
                        {
                            if (m_touchDragInProgress)
                            {
                                Drag = touchLocation.Position;
                            }
                            else if (Vector2.Distance(touchLocation.Position, m_touchStartPoint) > 0.2f * Widget.GlobalScale)//最小拖动距离
                            {
                                m_touchDragInProgress = true;
                                Drag = m_touchStartPoint;
                            }
                            if (!m_touchDragInProgress)
                            {
                                if (m_touchHoldInProgress)
                                {
                                    Hold = m_touchStartPoint;
                                    HoldTime = (float)(Time.FrameStartTime - m_touchStartTime);
                                }
                                else if (Time.FrameStartTime - m_touchStartTime > 0.2f)//最小按住时间
                                {
                                    m_touchHoldInProgress = true;
                                }
                            }
                        }
                    }
                }
                else if (touchLocation.State == TouchLocationState.Released && m_touchId == touchLocation.Id)
                {
                    if (!m_touchCleared)
                    {
                        Click = new Segment2(m_touchStartPoint, touchLocation.Position);
                    }
                    m_touchId = null;
                    m_touchCleared = false;
                    m_touchDragInProgress = false;
                    m_touchHoldInProgress = false;
                }
            }
        }
    }
}
